package org.xxpay.pay.channel.yeepay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.xxpay.core.common.domain.api.msg.AgentPayBalanceRetMsg;
import org.xxpay.core.common.domain.api.msg.QueryRetMsg;
import org.xxpay.core.common.util.AmountUtil;
import org.xxpay.core.common.util.DateUtil;
import org.xxpay.core.common.util.MyLog;
import org.xxpay.core.entity.BankCardBin;
import org.xxpay.core.entity.TransOrder;
import org.xxpay.pay.channel.BaseTrans;
import org.xxpay.pay.channel.yeepay.util.CallbackUtils;
import org.xxpay.pay.channel.yeepay.util.SeqUtil;
import org.xxpay.pay.channel.yeepay.util.SignUtil;
import org.xxpay.pay.channel.yeepay.util.XmlUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * 易宝代付发起代付， 订单查询， 账户余额查询
 */
@Service
public class YeepayTransService extends BaseTrans {

    private static final MyLog logger = MyLog.getLog(YeepayTransService.class);

    @Override
    public String getChannelName() {
        return YeepayConfig.CHANNEL_NAME_YEEPAY;
    }

    @Override
    public String getTransOrderId(TransOrder transOrder){
        return SeqUtil.compressTransOrderId(super.getTransOrderId(transOrder)); //将transOrderId 压缩到20位以内，并且为数字串格式，保存到数据库。
    }

    @Override
    public QueryRetMsg trans(TransOrder transOrder) {
        String logPrefix = "【易宝代付】";
        try {
            // [01]. 获取订单和基本配置信息
            String transOrderId = transOrder.getTransOrderId();
            YeepayConfig config = new YeepayConfig(getTransParam(transOrder)); //获取配置信息

            //[02]. 封装请求参数
            Map<String, String> reqXmlMap = new HashMap<>();
            reqXmlMap.put("cmd", "TransferSingle");	//命令： 固定值TransferSingle
            reqXmlMap.put("version", "1.1"); 	//接口版本： 固定值1.1
            reqXmlMap.put("group_Id", config.getGroupId()); 	//总公司编号：总公司在易宝支付的客户编号
            reqXmlMap.put("mer_Id", config.getMerId()); 	//实际发起付款的交易商户编号： 发起付款的总（分）公司在易宝支付的客户编号
            reqXmlMap.put("product", ""); 	//产品类型： 为空走代付、代发出款：值为“RJT” 走日结通出款
            reqXmlMap.put("batch_No", transOrderId); 	//打款批次号: 不区分产品，必须唯一，要求长度范围15-20位的数字串
            reqXmlMap.put("bank_Code", ""); 	//收款银行编号
            reqXmlMap.put("order_Id", transOrderId); 	//订单号
            reqXmlMap.put("cnaps", ""); 	//联行号

            BankCardBin bankCardBin = commonService.bankCardBinService.findByCardNo(transOrder.getAccountNo());
            reqXmlMap.put("bank_Name", bankCardBin == null ? "" : bankCardBin.getBankName().replace("\n","")); //收款银行全称

            reqXmlMap.put("branch_Bank_Name", ""); 	//收款银行支行名称
            reqXmlMap.put("amount", AmountUtil.convertCent2Dollar(transOrder.getAmount().toString())); 	//打款金额： 单位元
            reqXmlMap.put("account_Name", transOrder.getAccountName()); 	//账户名称
            reqXmlMap.put("account_Number", transOrder.getAccountNo()); 	//账户号
            reqXmlMap.put("account_Type", "pr"); 	//账户类型 对公：pu; 对私：pr ; 不填写默认为pr
            reqXmlMap.put("province", ""); 	//收款行省份编码
            reqXmlMap.put("city", ""); 	//收款行城市编码
            reqXmlMap.put("fee_Type", "SOURCE"); 	//手续费收取方式： “SOURCE”商户承担； “TARGET”用户承担
            reqXmlMap.put("payee_Email", ""); 	//收款人邮箱
            reqXmlMap.put("payee_Mobile", ""); 	//收款人电话
            reqXmlMap.put("urgency", "1"); 	//加急：1表示实时出款  0非实时出款。 当开通该功能，并且收款行是易宝实时出款银行才能实时出款
            reqXmlMap.put("leave_Word", ""); 	//留言
            reqXmlMap.put("abstractInfo", ""); 	//摘要
            reqXmlMap.put("remarksInfo", ""); 	//备注

            //签名字段及顺序： cmd、 mer_Id、 batch_No、order_Id、 amount、 account_Number、 mchHmacKey
            String signSource = SignUtil.genSignSource(
                    reqXmlMap.get("cmd"),
                    reqXmlMap.get("mer_Id"),
                    reqXmlMap.get("batch_No"),
                    reqXmlMap.get("order_Id"),
                    reqXmlMap.get("amount"),
                    reqXmlMap.get("account_Number"),
                    config.getHmacKey() );

            reqXmlMap.put("hmac", SignUtil.sign(signSource, getCertFilePath(getChannelName(), payConfig.getCertRootPath(), config.getPfxFilePath()), config.getPfxPassword())); 	//签名信息

            //[03]. 发送http请求易宝网关，并获取数据
            String responseMsg = CallbackUtils.httpRequest(config.getTransReqUrl(), XmlUtils.toXml(reqXmlMap), "POST", "gbk","text/xml ;charset=gbk", false);

            //[04]. 验签
            if(StringUtils.isEmpty(responseMsg)){ //无任何响应数据
                logger.info("{} >>> 请求失败", logPrefix);
                return QueryRetMsg.confirmFail();

            }

            Map<String,String>  resMap = XmlUtils.xml2Map(responseMsg); ///将响应结果转换为map

            String resCmd = resMap.get("cmd"); //命令
            String resRetCode = resMap.get("ret_Code"); //返回代码
            String resR1Code = resMap.get("r1_Code"); //打款状态码
            String resBankStatus = resMap.get("bank_Status"); //银行状态

            logger.info("响应结果transOrderId={}, cmd={}, ret_Code={}, r1_Code={}, bank_Status={}", transOrderId, resCmd, resRetCode, resR1Code, resBankStatus);

            //返回结果验签字段及顺序： cmd,ret_Code,r1_Code,hmacKey
            signSource = SignUtil.genSignSource(resCmd, resRetCode, resR1Code, config.getHmacKey());

            String signedContent = SignUtil.verifyHMAC(resMap.get("hmac")); //获取原始签名串
            if(StringUtils.isEmpty(signedContent) || !signedContent.equals(signSource)){ //验签失败

                logger.info("{} >>> 验签失败", logPrefix);
                return QueryRetMsg.unknown();
            }

            //[05]. 处理响应数据结果
            if("9999".equals(resRetCode)){ //易宝系统异常，需要再次查询
                logger.info("{} >>> 易宝系统异常，需要再次查询", logPrefix);
                return QueryRetMsg.waiting();
            }

            if(!"1".equals(resRetCode)){ //既不是1也不是9999 ，易宝未正常接收
                logger.info("{} >>> 既不是1也不是9999 ，易宝未正常接收", logPrefix);
                return QueryRetMsg.confirmFail();
            }

            //以下为 resRetCode = 1 的情况

            if("0025".equals(resR1Code) || "0029".equals(resR1Code) || "0030".equals(resR1Code)){ //处理中，需要再次查询
                logger.info("{} >>> 处理中，需要再次查询", logPrefix);
                return QueryRetMsg.waiting();
            }

            if("0027".equals(resR1Code) || "0028".equals(resR1Code)){ //资金退回账户，打款失败

                logger.info("{} >>> 资金退回账户，打款失败", logPrefix);
                return QueryRetMsg.confirmFail();

            }

            if("0026".equals(resR1Code)){ //将款打款到银行

                if("S".equals(resBankStatus)){ //明确成功

                    logger.info("{} >>> 明确成功", logPrefix);
                    return QueryRetMsg.confirmSuccess(null);
                }

                if("F".equals(resBankStatus)){ //明确失败

                    logger.info("{} >>> 明确失败", logPrefix);
                    return QueryRetMsg.confirmFail();
                }

                if("I".equals(resBankStatus) || "W".equals(resBankStatus) || "U".equals(resBankStatus)){ //处理中

                    logger.info("{} >>> 处理中", logPrefix);
                    return QueryRetMsg.waiting();
                }
            }

            return QueryRetMsg.waiting(); //其他状态 认为处理中
        }catch (Exception e) {
            logger.error(e, "转账异常");
            return QueryRetMsg.sysError();
        }
    }

    public QueryRetMsg query(TransOrder transOrder) {

        String logPrefix = "【易宝代付查询】";
        QueryRetMsg retObj = QueryRetMsg.waiting();
        try {
            // [01]. 获取订单和基本配置信息
            String transOrderId = transOrder.getTransOrderId();
            YeepayConfig config = new YeepayConfig(getTransParam(transOrder)); //获取配置信息

            //[02]. 封装请求参数
            Map<String, String> reqXmlMap = new HashMap<>();
            reqXmlMap.put("cmd", "BatchDetailQuery");	//命令： 固定值BatchDetailQuery
            reqXmlMap.put("version", "1.0"); 	//接口版本： 固定值1.0
            reqXmlMap.put("group_Id", config.getGroupId()); 	//总公司编号：总公司在易宝支付的客户编号
            reqXmlMap.put("mer_Id", config.getMerId()); 	//实际发起付款的交易商户编号： 发起付款的总（分）公司在易宝支付的客户编号
            reqXmlMap.put("query_Mode", "1"); 	//查询本公司自己发的交易
            reqXmlMap.put("product", ""); 	//产品类型： 为空走代付、代发出款：值为“RJT” 走日结通出款
            reqXmlMap.put("batch_No", transOrderId); 	//打款批次号: 不区分产品，必须唯一，要求长度范围15-20位的数字串
            reqXmlMap.put("order_Id", transOrderId); //订单号： 查询该批次下的该笔订单详情
            reqXmlMap.put("page_No", "1"); 	//页码

            //签名字段及顺序： cmd、 mer_Id、 batch_No、order_Id、 page_No、 mchHmacKey
            String signSource = SignUtil.genSignSource(
                    reqXmlMap.get("cmd"),
                    reqXmlMap.get("mer_Id"),
                    reqXmlMap.get("batch_No"),
                    reqXmlMap.get("order_Id"),
                    reqXmlMap.get("page_No"),
                    config.getHmacKey() );

            reqXmlMap.put("hmac", SignUtil.sign(signSource, getCertFilePath(getChannelName(), payConfig.getCertRootPath(), config.getPfxFilePath()), config.getPfxPassword())); 	//签名信息

            //[03]. 发送http请求易宝网关，并获取数据
            String responseMsg = CallbackUtils.httpRequest(config.getTransReqUrl(), XmlUtils.toXml(reqXmlMap), "POST", "gbk","text/xml ;charset=gbk", false);

            //[04]. 验签
            if(StringUtils.isEmpty(responseMsg)){ //无任何响应数据
                logger.info("{} >>> 请求失败", logPrefix);
                return QueryRetMsg.unknown();
            }

            Map<String,String>  resMap = XmlUtils.xml2Map(responseMsg); ///将响应结果转换为map

            String resCmd = resMap.get("cmd"); //命令
            String resRetCode = resMap.get("ret_Code"); //返回代码
            String resBatchNo = resMap.get("batch_No"); //batch_No 批次号
            String resTotalNum = resMap.get("total_Num"); //total_Num 返回记录数
            String resEndFlag = resMap.get("end_Flag"); //end_Flag 结束标志 Y全部输出 N未结束
            String resErrorMsg = resMap.get("error_Msg"); //错误信息描述

            logger.info("响应结果transOrderId={}, cmd={}, ret_Code={}, batch_No={}, total_Num={}, end_Flag={}, error_Msg={}", transOrderId, resCmd, resRetCode, resBatchNo, resTotalNum, resEndFlag, resErrorMsg);

            //返回结果验签字段及顺序： cmd, ret_Code, batch_No, total_Num, end_Flag, mchHmacKey
            signSource = SignUtil.genSignSource(resCmd, resRetCode, resBatchNo, resTotalNum, resEndFlag, config.getHmacKey());

            String signedContent = SignUtil.verifyHMAC(resMap.get("hmac")); //获取原始签名串
            if(StringUtils.isEmpty(signedContent) || !signedContent.equals(signSource)){ //验签失败

                logger.info("{} >>> 验签失败", logPrefix);
                return QueryRetMsg.unknown();
            }

            Map<String, String> orderMap = XmlUtils.xml2Map(responseMsg, "list.items.item");
            String resBankStatus = orderMap.get("bank_Status");
            String resR1Code = orderMap.get("r1_Code");
            String resFailDesc = orderMap.get("fail_Desc");
            String resOrderId = orderMap.get("order_Id");

            logger.info("响应订单结果transOrderId={}, order_Id={}, bank_Status={}, r1_Code={}, fail_Desc={}, ", transOrderId, resOrderId, resBankStatus, resR1Code, resFailDesc);
            String channelOriginResponse = JSON.toJSON(orderMap).toString(); //将结果记录到channelObj， 运营平台可查询返回接口返回数据。

            //查询订单号不匹配
            if(!transOrderId.equals(resOrderId)){
                logger.info("{} >>> 易宝系统返回订单号不匹配", logPrefix);
                retObj = QueryRetMsg.unknown();
                retObj.setChannelOriginResponse(channelOriginResponse);
                return retObj;
            }

            //[05]. 处理响应数据结果
            if("9999".equals(resRetCode)){ //易宝系统异常
                logger.info("{} >>> 易宝系统异常", logPrefix);
                retObj = QueryRetMsg.unknown();
                retObj.setChannelOriginResponse(channelOriginResponse);
                return retObj;
            }

            if(!"1".equals(resRetCode)){ //既不是1也不是9999 ，易宝未正常接收
                logger.info("{} >>> 既不是1也不是9999 ，易宝未正常接收", logPrefix);
                retObj = QueryRetMsg.unknown();
                retObj.setChannelOriginResponse(channelOriginResponse);
                return retObj;
            }

            //以下为 resRetCode = 1 的情况
            if("0025".equals(resR1Code) || "0029".equals(resR1Code) || "0030".equals(resR1Code)){ //处理中，需要再次查询
                logger.info("{} >>> 处理中，需要再次查询", logPrefix);
                retObj = QueryRetMsg.waiting();
                retObj.setChannelOriginResponse(channelOriginResponse);
                return retObj;

            }

            if("0027".equals(resR1Code) || "0028".equals(resR1Code)){ //资金退回账户，打款失败

                logger.info("{} >>> 资金退回账户，打款失败", logPrefix);
                String channelErrCode =  resR1Code + "|" + resBankStatus;    //返回错误代码
                String channelErrMsg = resFailDesc;    // 错误信息
                retObj = QueryRetMsg.confirmFail(null, channelErrCode, channelErrMsg);
                retObj.setChannelOriginResponse(channelOriginResponse);
                return retObj;
            }

            if("0026".equals(resR1Code)){ //将款打款到银行

                if("S".equals(resBankStatus)){ //明确成功

                    logger.info("{} >>> 明确成功", logPrefix);
                    retObj = QueryRetMsg.confirmSuccess(null);
                    retObj.setChannelOriginResponse(channelOriginResponse);
                    return retObj;
                }

                if("F".equals(resBankStatus)){ //明确失败

                    logger.info("{} >>> 明确失败", logPrefix);
                    String channelErrCode =  resR1Code + "|" + resBankStatus;    //返回错误代码
                    String channelErrMsg = resFailDesc;    // 错误信息
                    retObj = QueryRetMsg.confirmFail(null, channelErrCode, channelErrMsg);
                    retObj.setChannelOriginResponse(channelOriginResponse);
                    return retObj;

                }

                if("I".equals(resBankStatus) || "W".equals(resBankStatus) || "U".equals(resBankStatus)){ //处理中

                    logger.info("{} >>> 处理中", logPrefix);
                    retObj = QueryRetMsg.waiting();
                    retObj.setChannelOriginResponse(channelOriginResponse);
                    return retObj;
                }
            }

            return retObj;
        }catch (Exception e) {
            logger.error(e, "转账异常");
            return QueryRetMsg.sysError();
        }
    }


    @Override
    public AgentPayBalanceRetMsg balance(String payParam) {

        String logPrefix = "【易宝余额查询】";
        JSONObject retObj = buildRetObj();
        try {
            // [01]. 获取基本配置信息
            YeepayConfig config = new YeepayConfig(payParam); //获取配置信息

            //[02]. 封装请求参数
            Map<String, String> reqXmlMap = new HashMap<>();
            reqXmlMap.put("cmd", "AccountBalanaceQuery");	//命令： 固定值AccountBalanaceQuery   Balanace ? = =/
            reqXmlMap.put("version", "1.0"); 	//接口版本： 固定值1.0
            reqXmlMap.put("mer_Id", config.getMerId()); 	//实际发起付款的交易商户编号： 发起付款的总（分）公司在易宝支付的客户编号
            reqXmlMap.put("date", DateUtil.getCurrentTimeStr(DateUtil.FORMAT_YYYY_MM_DD)); 	//日期：查询输入日期的账户余额

            //签名字段及顺序： cmd、 mer_Id、 date、 mchHmacKey
            String signSource = SignUtil.genSignSource(
                    reqXmlMap.get("cmd"),
                    reqXmlMap.get("mer_Id"),
                    reqXmlMap.get("date"),
                    config.getHmacKey() );

            reqXmlMap.put("hmac", SignUtil.sign(signSource, getCertFilePath(getChannelName(), payConfig.getCertRootPath(), config.getPfxFilePath()), config.getPfxPassword())); 	//签名信息

            //[03]. 发送http请求易宝网关，并获取数据
            String responseMsg = CallbackUtils.httpRequest(config.getQueryBalanceUrl(), XmlUtils.toXml(reqXmlMap), "POST", "gbk","text/xml ;charset=gbk", false);

            //[04]. 验签
            if(StringUtils.isEmpty(responseMsg)){ //无任何响应数据
                logger.info("{} >>> 请求失败", logPrefix);
                return AgentPayBalanceRetMsg.fail();
            }

            Map<String,String>  resMap = XmlUtils.xml2Map(responseMsg); ///将响应结果转换为map

            String resCmd = resMap.get("cmd"); //命令
            String resRetCode = resMap.get("ret_Code"); //返回码
            String resErrorMsg = resMap.get("error_msg"); //错误信息描述
            String resBalanceAmount = resMap.get("balance_Amount"); //账户余额
            String resValidAmount = resMap.get("valid_Amount"); //可用打款余额

            logger.info("响应结果 cmd={}, ret_Code={}, error_msg={}, balance_Amount={}, valid_Amount={}", resCmd, resRetCode, resErrorMsg, resBalanceAmount, resValidAmount);

            //返回结果验签字段及顺序： cmd,ret_Code,r1_Code,hmacKey
            signSource = SignUtil.genSignSource(resCmd, resRetCode, resBalanceAmount, resValidAmount, config.getHmacKey());

            String signedContent = SignUtil.verifyHMAC(resMap.get("hmac")); //获取原始签名串
            if(StringUtils.isEmpty(signedContent) || !signedContent.equals(signSource)){ //验签失败

                logger.info("{} >>> 验签失败", logPrefix);
                return AgentPayBalanceRetMsg.fail();
            }

            //[05]. 处理响应数据结果
            if("9999".equals(resRetCode)){ //易宝系统异常
                logger.info("{} >>> 易宝系统异常", logPrefix);
                return AgentPayBalanceRetMsg.fail();
            }

            return AgentPayBalanceRetMsg.success(
                    Long.parseLong(AmountUtil.convertDollar2Cent(resBalanceAmount)), // 现金余额
                    Long.parseLong(AmountUtil.convertDollar2Cent(resValidAmount)) // 代付可用余额
            );

        }catch (Exception e) {
            logger.error(e, "查询头寸异常");
            return AgentPayBalanceRetMsg.fail();
        }
    }
}
